# -*- coding: utf-8 -*-
"""
参数解析和验证服务

@author: AI Assistant
"""
import re
from typing import Dict, Any, List, Optional, Tuple
from loguru import logger
import copy


class ParameterService:
    """参数服务类 - 处理参数解析、验证和合并"""
    
    def __init__(self):
        """初始化参数服务"""
        pass
    
    def parse_parameter_changes(self, llm_response: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        """解析 LLM 响应中的参数修改
        
        Args:
            llm_response: LLM 响应字典
            
        Returns:
            参数修改字典，格式：{"path.to.param": value}
        """
        return llm_response.get("parameter_changes")
    
    def validate_parameters(
        self,
        parameter_changes: Dict[str, Any],
        para_schema: Dict[str, Any]
    ) -> Tuple[bool, List[Dict[str, Any]], List[Dict[str, Any]]]:
        """验证参数修改的合法性
        
        Args:
            parameter_changes: 参数修改，格式：{"initial_state.param": value}
            para_schema: 参数模式定义
            
        Returns:
            (是否全部合法, 错误列表, 警告列表)
        """
        errors = []
        warnings = []
        
        for param_path, new_value in parameter_changes.items():
            # 解析参数路径
            # 支持格式：
            # 1. category.parameter_name (简单参数)
            # 2. category.parameter_name[index].field (数组元素)
            parts = param_path.split(".")
            
            # 提取类别和参数名（可能包含数组索引）
            if len(parts) < 2:
                errors.append({
                    "parameter": param_path,
                    "message": "参数路径格式错误，应为 'category.parameter_name' 或 'category.parameter_name[index].field'",
                    "value": new_value
                })
                continue
            
            category = parts[0]
            param_name_with_index = parts[1]
            
            # 提取参数名（去除数组索引）
            # 例如：monthlyPET[5] -> monthlyPET
            param_name_match = re.match(r'^([^\[]+)', param_name_with_index)
            param_name = param_name_match.group(1) if param_name_match else param_name_with_index
            
            # 获取参数定义
            param_def = para_schema.get(category, {}).get(param_name)
            if not param_def:
                errors.append({
                    "parameter": param_path,
                    "message": f"参数不存在于模式定义中",
                    "value": new_value
                })
                continue
            
            # 检查是否是数组元素修改（包含索引）
            is_array_element = '[' in param_name_with_index
            
            # 验证类型
            expected_type = param_def.get("type", "float")
            
            # 对于数组元素修改，跳过类型检查（因为修改的是元素的某个字段，不是整个数组）
            if is_array_element:
                logger.debug(f"数组元素修改，跳过类型检查: {param_path}")
            else:
                # 检查实际值的类型
                actual_type = type(new_value).__name__
                
                # 如果实际值是数组或字典，自动调整期望类型
                if isinstance(new_value, list):
                    expected_type = "array"
                    logger.debug(f"检测到数组类型，自动调整: {param_path}")
                elif isinstance(new_value, dict):
                    expected_type = "object"
                    logger.debug(f"检测到对象类型，自动调整: {param_path}")
                
                # 验证类型
                if not self._check_type(new_value, expected_type):
                    errors.append({
                        "parameter": param_path,
                        "message": f"参数类型错误，期望 {expected_type}，实际 {actual_type}",
                        "value": new_value
                    })
                    continue
            
            # 对于数组和对象类型，或数组元素修改，跳过范围检查
            if expected_type in ["array", "object"] or is_array_element:
                logger.debug(f"跳过范围检查: {param_path} (类型: {expected_type}, 数组元素: {is_array_element})")
                continue
            
            # 验证范围（仅适用于数值类型）
            min_val = param_def.get("min")
            max_val = param_def.get("max")
            
            if min_val is not None and new_value < min_val:
                errors.append({
                    "parameter": param_path,
                    "message": f"参数值小于最小值 {min_val}",
                    "value": new_value,
                    "valid_range": [min_val, max_val]
                })
            elif max_val is not None and new_value > max_val:
                errors.append({
                    "parameter": param_path,
                    "message": f"参数值大于最大值 {max_val}",
                    "value": new_value,
                    "valid_range": [min_val, max_val]
                })
            else:
                # 检查是否接近边界（警告）
                if min_val is not None and max_val is not None:
                    range_size = max_val - min_val
                    if new_value < min_val + range_size * 0.1:
                        warnings.append({
                            "parameter": param_path,
                            "message": "参数值接近下限",
                            "value": new_value,
                            "valid_range": [min_val, max_val]
                        })
                    elif new_value > max_val - range_size * 0.1:
                        warnings.append({
                            "parameter": param_path,
                            "message": "参数值接近上限",
                            "value": new_value,
                            "valid_range": [min_val, max_val]
                        })
        
        is_valid = len(errors) == 0
        return is_valid, errors, warnings
    
    def _check_type(self, value: Any, expected_type: str) -> bool:
        """检查值的类型是否匹配
        
        Args:
            value: 要检查的值
            expected_type: 期望的类型（'float', 'int', 'str', 'bool', 'array', 'object'）
            
        Returns:
            bool: 类型匹配返回 True
        """
        type_map = {
            "float": (int, float),
            "int": int,
            "str": str,
            "bool": bool,
            "array": list,
            "object": dict
        }
        
        expected_python_type = type_map.get(expected_type, (int, float))
        return isinstance(value, expected_python_type)
    
    def merge_parameters(
        self,
        original: Dict[str, Any],
        updates: Dict[str, Any]
    ) -> Dict[str, Any]:
        """合并参数更新到原始参数
        
        Args:
            original: 原始参数字典，格式：{"initial_state": {...}, "parameters": {...}}
            updates: 参数更新，格式：{"initial_state.param": value}
            
        Returns:
            合并后的参数字典
        """
        result = copy.deepcopy(original)
        
        for param_path, new_value in updates.items():
            parts = param_path.split(".")
            if len(parts) < 2:
                logger.warning(f"跳过无效的参数路径: {param_path}")
                continue
            
            category = parts[0]
            param_name_with_index = parts[1]
            
            # 确保类别存在
            if category not in result:
                result[category] = {}
            
            # 检查是否是数组元素修改
            # 例如：parameters.monthlyPET[5].pet
            array_match = re.match(r'^([^\[]+)\[(\d+)\]$', param_name_with_index)
            
            if array_match and len(parts) == 3:
                # 数组元素的字段修改
                param_name = array_match.group(1)
                index = int(array_match.group(2))
                field_name = parts[2]
                
                # 确保数组存在
                if param_name not in result[category]:
                    logger.warning(f"参数不存在: {category}.{param_name}")
                    continue
                
                # 确保是数组
                if not isinstance(result[category][param_name], list):
                    logger.warning(f"参数不是数组: {category}.{param_name}")
                    continue
                
                # 确保索引有效
                if index >= len(result[category][param_name]):
                    logger.warning(f"数组索引超出范围: {param_path}")
                    continue
                
                # 更新数组元素的字段
                result[category][param_name][index][field_name] = new_value
                logger.debug(f"更新数组元素: {param_path} = {new_value}")
                
            elif len(parts) == 2:
                # 简单参数修改
                # 提取参数名（去除可能的数组索引）
                param_name_match = re.match(r'^([^\[]+)', param_name_with_index)
                param_name = param_name_match.group(1) if param_name_match else param_name_with_index
                
                # 更新参数值
                result[category][param_name] = new_value
                logger.debug(f"更新参数: {param_path} = {new_value}")
            else:
                logger.warning(f"不支持的参数路径格式: {param_path}")
        
        return result
    
    def extract_parameters(self, full_params: Dict[str, Any]) -> Tuple[Dict[str, Any], Dict[str, Any]]:
        """从完整参数字典中提取初始状态和模型参数
        
        Args:
            full_params: 完整参数字典
            
        Returns:
            (initial_state, parameters)
        """
        initial_state = full_params.get("initial_state", {})
        parameters = full_params.get("parameters", {})
        return initial_state, parameters


# 使用示例
if __name__ == "__main__":
    service = ParameterService()
    
    # 测试参数验证
    para_schema = {
        "initial_state": {
            "water_level": {
                "type": "float",
                "min": 0.0,
                "max": 200.0,
                "unit": "m"
            }
        }
    }
    
    parameter_changes = {
        "initial_state.water_level": 100.5
    }
    
    is_valid, errors, warnings = service.validate_parameters(parameter_changes, para_schema)
    print(f"验证结果: {'✅ 通过' if is_valid else '❌ 失败'}")
    if errors:
        print(f"错误: {errors}")
    if warnings:
        print(f"警告: {warnings}")

